package com.rongji.dfish.base.tree;

import java.util.*;
import java.util.function.Predicate;

/**
 * 对TreeNode 搜索的封装，简易调用
 * @param <E>
 */
public class Search<E> implements Iterable<TreeNode<E>> {
    private Set<TreeNode<E>> result;

    /**
     * 构造函数
     */
    protected Search() {
        result = Collections.emptySet();
    }

    /**
     * 构造函数
     * @param node TreeNode 目标范围的根节点
     */
    protected Search(TreeNode<E> node) {
        result = new LinkedHashSet<>(1);
        result.add(node);
    }


    /**
     * 找到第一个符合条件的子节点。深度优先，查找范围包括当前节点
     * 和java.util.Stream的findFirst相当
     *
     * @param filter Predicate 条件
     * @return Search 找到的结果
     */
    public Search<E> find(Predicate<TreeNode<E>> filter) {
        for (TreeNode<E> node : result) {
            TreeNode<E> hit;
            if ((hit = find(filter, node)) != null) {
                return new Search<>(hit);
            }
        }
        return new Search<>();
    }

    /**
     * 根据 ID 搜索。这个ID是指TreeNode的getId();
     * 而非逻辑ID 。如果是逻辑ID。请用
     * find((node)->return id.equals(node.getData().getId()));
     *
     * @param id String
     * @return Search 找到的结果
     */
    public Search<E> findById(final String id) {
        return find(node -> id != null && id.equals(node.getId()));
    }


    private static<E> TreeNode<E> find(Predicate<TreeNode<E>> filter, TreeNode<E> node) {
        if (node == null) {
            return null;
        }
        if (filter.test(node)) {
            return node;
        }
        if (node.getChildren() != null) {
            TreeNode<E> hit;
            for (TreeNode<E> child : node.getChildren()) {
                if ((hit = find(filter, child)) != null) {
                    return hit;
                }
            }
        }
        return null;
    }

    /**
     * 向上找到第一个符合条件的子节点。查找范围包括当前节点
     *
     * @param filter Predicate 条件
     * @return Search 找到的结果
     */
    public Search<E> closest(Predicate<TreeNode<E>> filter) {
        TreeNode<E> hit;
        for (TreeNode<E> node : result) {
            if ((hit = closest(filter, node)) != null) {
                return new Search<>(hit);
            }
        }
        return new Search<>();
    }

    private static<E> TreeNode<E> closest(Predicate<TreeNode<E>> filter, TreeNode<E> node) {
        if (node == null) {
            return null;
        }
        if (filter.test(node)) {
            return node;
        }
        if (node.parent() != null) {
            TreeNode<E> hit;
            if ((hit = closest(filter, node.parent())) != null) {
                return hit;
            }
        }
        return null;
    }

    /**
     * 找到符合条件的子节点。深度优先，查找范围包括当前节点
     * 和java.util.Stream的findAny相当
     *
     * @param filter Predicate 条件
     * @return Search 找到的结果
     */
    public Search<E> findAll(Predicate<TreeNode<E>> filter) {
        Search<E> ret = new Search<>();
        ret.result = new LinkedHashSet<>();
        for (TreeNode<E> node : result) {
            findAll(filter, node, ret.result);
        }
        return ret;
    }

    private static<E> void findAll(Predicate<TreeNode<E>> filter, TreeNode<E> node, Set<TreeNode<E>> result) {
        if (node == null) {
            return;
        }
        if (filter.test(node)) {
            result.add(node);
        }
        if (node.getChildren() != null) {
            for (TreeNode<E> child : node.getChildren()) {
                findAll(filter, child, result);
            }
        }
    }


    @Override
    public Iterator<TreeNode<E>> iterator() {
        return result.iterator();
    }

    /**
     * 当做当前节点只有一个结果，并获取结果
     *
     * @return TreeNode 结果
     */
    public TreeNode<E> get() {
        return result == null || result.size() == 0 ? null : result.iterator().next();
    }

    /**
     * 当做当前节点只有一个结果，并获取结果 的 data
     *
     * @return E 结果
     */
    public E getData() {
        TreeNode<E> n = get();
        return n == null ? null : n.getData();
    }

    /**
     * 当做当前节点只有一个结果，并获取结果 的 parent
     *
     * @return TreeNode 父节点
     */
    public TreeNode<E> parent() {
        TreeNode<E> n = get();
        return n == null ? null : n.parent();
    }

    /**
     * 当做当前节点只有一个结果，并获取结果 的 children
     *
     * @return List 子节点
     */
    public List<TreeNode<E>> getChildren() {
        TreeNode<E> n = get();
        return n == null ? null : n.getChildren();
    }

    /**
     * 当做当前节点只有一个结果，并获取结果 的 level
     * 如果没有则返回-1
     *
     * @return int level
     */
    public int getLevel() {
        TreeNode<E> n = get();
        return n == null ? -1 : n.getLevel();
    }



    /**
     * 当做当前节点只有一个结果，并获取结果 的id
     *
     * @return String id
     */
    public String getId() {
        TreeNode<E> n = get();
        return n == null ? null : n.getId();
    }


    /**
     * 取当前节点的顶级节点。
     *
     * @return Search 顶级节点
     */
    public Search<E> root() {
        return closest((node) -> node.parent() == null);
    }


}
